namespace dshow.Core
{
	using System;
	using System.Runtime.InteropServices;

  [ComImport,
    Guid("89c31040-846b-11ce-97d3-00aa0055595a"),
    InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  public interface IEnumMediaTypes
  {
    [PreserveSig]
    int Next(
        [In] int cMediaTypes,
        [In, Out, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(EMTMarshaler), SizeParamIndex = 0)] AMMediaType[] ppMediaTypes,
        [Out] out int pcFetched
        );

    [PreserveSig]
    int Skip([In] int cMediaTypes);

    [PreserveSig]
    int Reset();

    [PreserveSig]
    int Clone([Out] out IEnumMediaTypes ppEnum);
  }
  internal class EMTMarshaler : DsMarshaler
  {
    public EMTMarshaler(string cookie)
      : base(cookie)
    {
    }

    // Called just after invoking the COM method.  The IntPtr is the same one that just got returned
    // from MarshalManagedToNative.  The return value is unused.
    override public object MarshalNativeToManaged(IntPtr pNativeData)
    {
      AMMediaType[] emt = m_obj as AMMediaType[];

      for (int x = 0; x < emt.Length; x++)
      {
        // Copy in the value, and advance the pointer
        IntPtr p = Marshal.ReadIntPtr(pNativeData, x * IntPtr.Size);
        if (p != IntPtr.Zero)
        {
          emt[x] = (AMMediaType)Marshal.PtrToStructure(p, typeof(AMMediaType));
        }
        else
        {
          emt[x] = null;
        }
      }

      return null;
    }

    // The number of bytes to marshal out
    override public int GetNativeDataSize()
    {
      // Get the array size
      int i = ((Array)m_obj).Length;

      // Multiply that times the size of a pointer
      int j = i * IntPtr.Size;

      return j;
    }

    // This method is called by interop to create the custom marshaler.  The (optional)
    // cookie is the value specified in MarshalCookie="asdf", or "" is none is specified.
    public static ICustomMarshaler GetInstance(string cookie)
    {
      return new EMTMarshaler(cookie);
    }
  }

  // This abstract class contains definitions for use in implementing a custom marshaler.
  //
  // MarshalManagedToNative() gets called before the COM method, and MarshalNativeToManaged() gets
  // called after.  This allows for allocating a correctly sized memory block for the COM call,
  // then to break up the memory block and build an object that c# can digest.

  abstract internal class DsMarshaler : ICustomMarshaler
  {
    #region Data Members
    // The cookie isn't currently being used.
    protected string m_cookie;

    // The managed object passed in to MarshalManagedToNative, and modified in MarshalNativeToManaged
    protected object m_obj;
    #endregion

    // The constructor.  This is called from GetInstance (below)
    public DsMarshaler(string cookie)
    {
      // If we get a cookie, save it.
      m_cookie = cookie;
    }

    // Called just before invoking the COM method.  The returned IntPtr is what goes on the stack
    // for the COM call.  The input arg is the parameter that was passed to the method.
    virtual public IntPtr MarshalManagedToNative(object managedObj)
    {
      // Save off the passed-in value.  Safe since we just checked the type.
      m_obj = managedObj;

      // Create an appropriately sized buffer, blank it, and send it to the marshaler to
      // make the COM call with.
      int iSize = GetNativeDataSize() + 3;
      IntPtr p = Marshal.AllocCoTaskMem(iSize);

      for (int x = 0; x < iSize / 4; x++)
      {
        Marshal.WriteInt32(p, x * 4, 0);
      }

      return p;
    }

    // Called just after invoking the COM method.  The IntPtr is the same one that just got returned
    // from MarshalManagedToNative.  The return value is unused.
    virtual public object MarshalNativeToManaged(IntPtr pNativeData)
    {
      return m_obj;
    }

    // Release the (now unused) buffer
    virtual public void CleanUpNativeData(IntPtr pNativeData)
    {
      if (pNativeData != IntPtr.Zero)
      {
        Marshal.FreeCoTaskMem(pNativeData);
      }
    }

    // Release the (now unused) managed object
    virtual public void CleanUpManagedData(object managedObj)
    {
      m_obj = null;
    }

    // This routine is (apparently) never called by the marshaler.  However it can be useful.
    abstract public int GetNativeDataSize();

    // GetInstance is called by the marshaler in preparation to doing custom marshaling.  The (optional)
    // cookie is the value specified in MarshalCookie="asdf", or "" is none is specified.

    // It is commented out in this abstract class, but MUST be implemented in derived classes
    //public static ICustomMarshaler GetInstance(string cookie)
  }

	// IPin interface
	//
	// The IPin interface represents a single, unidirectional
	// connection point on a filter
	//
	[ComImport,
	Guid("56A86891-0AD4-11CE-B03A-0020AF0BA770"),
	InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
	public interface IPin
	{
		// Connects the pin to another pin
		[PreserveSig]
		int Connect(
			[In] IPin pReceivePin,
			[In, MarshalAs(UnmanagedType.LPStruct)] AMMediaType	pmt);

		// Accepts a connection from another pin
		[PreserveSig]
		int ReceiveConnection(
			[In] IPin pReceivePin,
			[In, MarshalAs(UnmanagedType.LPStruct)] AMMediaType	pmt);

		// Breaks the current pin connection
		[PreserveSig]
		int Disconnect();

		// Retrieves the pin connected to this pin
		[PreserveSig]
		int ConnectedTo(
			[Out] out IPin ppPin);

		// Retrieves the media type for the current pin connection
		[PreserveSig]
		int ConnectionMediaType(
			[Out, MarshalAs(UnmanagedType.LPStruct)] AMMediaType pmt);

		// Retrieves information about the pin, such as the name,
		// the owning filter, and the direction
		[PreserveSig]
		int QueryPinInfo(
			[Out, MarshalAs(UnmanagedType.LPStruct)] PinInfo pInfo);

		// Retrieves the direction of the pin (input or output)
		[PreserveSig]
		int QueryDirection(
			out PinDirection pPinDir);

		// Retrieves the pin identifier
		[PreserveSig]
		int QueryId(
			[Out, MarshalAs(UnmanagedType.LPWStr)] out string Id);

		// Determines whether the pin accepts a specified media type
		[PreserveSig]
		int QueryAccept(
			[In, MarshalAs(UnmanagedType.LPStruct)] AMMediaType	pmt);

		// Enumerates the pin's preferred media types
    //[PreserveSig]
    //int EnumMediaTypes(
    //  IntPtr ppEnum);
    [PreserveSig]
    int EnumMediaTypes([Out] out IntPtr ppEnum);

		// Retrieves the pins that are connected
		// privately to this pin (within the filter)
		[PreserveSig]
		int QueryInternalConnections(
			IntPtr apPin,
			[In, Out] ref int nPin);

		// Notifies the pin that no additional data is expected
		[PreserveSig]
		int EndOfStream();

		// Begins a flush operation
		[PreserveSig]
		int BeginFlush();

		// Ends a flush operation
		[PreserveSig]
		int EndFlush();

		// Notifies the pin that media samples received after
		// this call are grouped as a segment
		[PreserveSig]
		int NewSegment(
			long tStart,
			long tStop,
			double dRate);
	}
}
